 /*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
 import java.applet.*;
 import java.awt.*;
 import java.awt.event.*;
 import java.text.*;
/**
 * Copyright 2011 Nick Stanish
 * this program is distributed under the terms of the GNU General Public License v3
 *
 **/
  public class PS extends Applet implements Runnable, MouseListener,MouseMotionListener,ActionListener, ItemListener, KeyListener {
    private static final long serialVersionUID = 1L;
	   	//************************
	   	//* start custom methods *
	   	//************************
	   	private double restitutionEq(double finalVelocity, double restitution){
			double newVelocity = finalVelocity * restitution;
			newVelocity = inverse(newVelocity);
			return newVelocity;
		}
	   	private double finalVelocityEq(double velocity, double gravity, double time){
			double finalVelocity = velocity + (gravity * time);
			finalVelocity = formatDecimal(2,finalVelocity);
			if(finalVelocity < 0){
				finalVelocity = inverse(finalVelocity);
			}
			return finalVelocity;
		}
	   	private double timer(double startTime, double refreshRate){
			double time = startTime + refreshRate;
			time = formatDecimal(3, time);
			return time;
		}
	   	private int traveledDistanceEq(int scale,int start, double velocity, double gravity, double time){
			double distance;
			distance = (start * scale) + (velocity * time) + ((gravity/2) * power(time, 2));
			distance /= scale;
			int traveled = formatInt(distance);
			return traveled;
		}
	   	private double inverse(double num){
			num = 0 - num;
			return num;
		}
	   	private double power(double num, int x){
			double result;
			result = num;
			for(int a = 1; a < x; a++){
				result *= num;
			}
			if (x == 0){
				result = 1;
			}
			return result;
		}
	   	private String formatTime(double seconds){
	   		String time;
	   		double minutes = 0;
	   		if (seconds >= 60){
				minutes = seconds / 60;
				seconds %= 60;
	   		}
	   		time = formatInt(minutes) + ":"+ formatDecimal(1,seconds);
	   		return time;
	   	}
	   	private void playSound(AudioClip soundClip){
	   		if(sound){
				soundClip.play();
	   		}

	   	}
	   	private double formatDecimal(int x, double formatThis){
	   		if (x > 0){
				int count = 0;
				String decimals = "#.";
				while(count < x){
						decimals += "#";
						count++;
				}
				DecimalFormat form = new DecimalFormat(decimals);
				formatThis = Double.valueOf(form.format(formatThis));
	   		}
	   		return formatThis;

	   	}
	   	private int formatInt(double formatThis){
	   		int formatted = 0;
	   		DecimalFormat form = new DecimalFormat("#");
	   		formatted = Integer.valueOf(form.format(formatThis));
	   		return formatted;
	   }
	   private void reset(){
		   for(int x = 0; x < objects; x++){
			   mainTimer=0;
			   objX[x] = startX[x];
		   	   objY[x] = startY[x];
		   	   currentVelocity[x] = 0;
		   	   bounceVelocity[x] = 0;
		   	   bounces[x] = 0;
		   	   firstDrop[x] = true;
		   	   time[x] = 0;
		   	   stopped[x] = false;
		   	   velocity[x] = 0;
		  }

	   }
	   //**********************
	   //* end custom methods *
	   //**********************
	   /**
	   *
	   * declarations
	   *
	   **/
	   Thread th = new Thread (this);
	   int default_scale = 1;
	   double default_restitution = .5;
	   double default_gravity = 9.8;
	   double default_spf = .032;
	   Frame window = new frame("Options");
	   Image background, mImage, popup, clear, play, pause, sound_on, sound_off, ball;
	   int scale, platformHeight, ground;
	   int objectFocused = 0;
	   int objects = 5;
	   int objHeight = 20;
	   int objWidth = 20;
	   double gravity, spf;
	   int[] objX = new int[objects];
	   int[] objY = new int[objects];
	   int[] startX = new int[objects];
	   int[] startY = new int[objects];
	   int[] bounces = new int[objects];
	   double mainTimer = 0;
	   double[] time = new double[objects];
	   double[] restitution = new double[objects];
	   double[] velocity = new double[objects];
	   double[] currentVelocity = new double[objects];
	   double[] bounceVelocity = new double[objects];
	   Button[] enterButton = new Button[objects];
	   Label[] label = new Label[objects];
	   Label objectSelectedLabel;
	   Panel[] row = new Panel[objects];
	   Panel objectSelectedPanel;
	   boolean[] active = new boolean[objects];
	   boolean[] stopped = new boolean[objects];
	   boolean[] firstDrop = new boolean[objects];
	   Checkbox activatedBox = new Checkbox("Activated",false);
	   Checkbox bounceBox = new Checkbox("",true);
 	   TextField[] textBox = new TextField[objects];
 	   boolean sound, platformSelected, started;
 	   Menu objectMenu;
 	   MenuItem[] obj = new MenuItem[objects];
 	   AudioClip appearSound, stopSound, springSound, resetSound, dropSound, hitSound;
	   /**
	   * end declarations
	   **/
	   public void init() {
		  gravity = default_gravity;
		  spf = default_spf;
	  	  scale = default_scale;
	  	  ground = 550;
	  	  platformHeight = 250;
	  	  sound = true;
	      started = false;
	  	  platformSelected = false;
	  	  addMouseListener(this);
		  addMouseMotionListener(this);
		  addKeyListener(this);
		  setLayout(null);
		  setBackground (Color.white);
		  objectMenu = new Menu("Object");
		  window.getMenuBar().add(objectMenu);
		  //object fun
		  for(int x = 0; x < objects; x++){
		  obj[x] = new MenuItem("" + (x + 1));
		  obj[x].addActionListener(this);
		  objectMenu.add(obj[x]);
		  restitution[x] = default_restitution;
		  objX[x] = 250;
		  objY[x] = 250;
		  startX[x] = 250;
		  startY[x] = 250;
		  currentVelocity[x] = 0;
		  bounceVelocity[x] = 0;
		  bounces[x] = 0;
		  firstDrop[x] = true;
		  active[x] = false;
		  time[x] = 0;
		  stopped[x] = false;
		  velocity[x] = 0;
		  }
		  active[0]=true;
		  //images
		  popup = getImage(getCodeBase(), "media/popup.png");
		  clear = getImage(getCodeBase(), "media/clear.png");
		  play = getImage(getCodeBase(), "media/play.png");
		  pause = getImage(getCodeBase(), "media/pause.png");
		  sound_on = getImage(getCodeBase(), "media/sound_on.png");
		  sound_off = getImage(getCodeBase(), "media/sound_off.png");
		  background = getImage (getCodeBase(), "media/background.png");
		  ball = getImage (getCodeBase(), "media/ball.png");
		  //sounds
		  appearSound = getAudioClip(getCodeBase(), "media/appear.au");
		  stopSound = getAudioClip(getCodeBase(), "media/stop.au");
		  springSound = getAudioClip(getCodeBase(), "media/spring.au");
		  resetSound = getAudioClip(getCodeBase(), "media/reset.au");
		  dropSound = getAudioClip(getCodeBase(), "media/drop.au");
		  hitSound = getAudioClip(getCodeBase(), "media/hit.au");
		  //for window frame options
		  label[0] = new Label("Velocity");
		  label[1] = new Label("Restitution");
		  label[2] = new Label("Gravity");
		  label[3] = new Label("Scale");
		  label[4] = new Label("SPF");
		  activatedBox.addItemListener(this);
		  objectSelectedLabel = new Label("Object Selected: " + (objectFocused + 1));
		  objectSelectedPanel = new Panel();
		  objectSelectedPanel.setLayout(new FlowLayout(FlowLayout.LEADING));
		  objectSelectedPanel.add(objectSelectedLabel);
		  objectSelectedPanel.add(activatedBox);
		  window.add(objectSelectedPanel);
		  textBox[0] = new TextField(velocity[objectFocused] + "", 8);
		  textBox[1] = new TextField(restitution[objectFocused] + "", 8);
		  textBox[2] = new TextField(gravity + "", 8);
		  textBox[3] = new TextField(scale + "", 8);
		  textBox[4] = new TextField(spf + "", 8);
		  for(int x = 0;x<  5; x++){
		  	enterButton[x] = new Button("Enter");
		  	row[x] = new Panel();
		  	row[x].setLayout(new FlowLayout(FlowLayout.TRAILING));

		  	if (x==1){
		  			  row[x].add(bounceBox);
		  		  }
		  	enterButton[x].addActionListener(this);
		  	textBox[x].addActionListener(this);
		  	row[x].add(label[x]);
		  	row[x].add(textBox[x]);
		  	row[x].add(enterButton[x]);
		  	window.add(row[x]);


	 	}
	   }
	   public void itemStateChanged(ItemEvent ie){ //action listen for activated box
		   if (ie.getSource() == activatedBox){
		   				active[objectFocused] = activatedBox.getState();

			}

	   }
	   public void actionPerformed(ActionEvent evt) { //action listener for all objects
		   for(int x = 0; x < objects; x++){ //any of the menu objects
		   		 if (evt.getSource() == obj[x]){
				 objectFocused = x;
				 objectSelectedLabel.setText("Object Selected: " + (objectFocused + 1));
				 activatedBox.setState(active[x]);
				 textBox[0].setText(velocity[x] + "");
				 textBox[1].setText(restitution[x] + "");
				 activatedBox.setState(active[x]);

				 }
		  }
		   if (evt.getSource() == textBox[0] || evt.getSource() == enterButton[0]){ //velocity box and button
		   try {
			       velocity[objectFocused] = Double.parseDouble(textBox[0].getText());
				} catch(Exception e) { System.err.print("Velocity entered is invalid");}
			}
			else if (evt.getSource() == textBox[1] || evt.getSource() == enterButton[1]){ //restitution box and button
					   try {
						       restitution[objectFocused] = Double.parseDouble(textBox[1].getText());
							} catch(Exception e) { System.err.print("Restitution entered is invalid");}
			}
			else if (evt.getSource() == textBox[2] || evt.getSource() == enterButton[2]){ //gravity box and button
					   try {
						       gravity = Double.parseDouble(textBox[2].getText());
							} catch(Exception e) { System.err.print("Gravity entered is invalid");}
			}
			if (evt.getSource() == textBox[3] || evt.getSource() == enterButton[3]){ //scale box and button
					   try {
						       scale = Integer.parseInt(textBox[3].getText());
						       if (scale < 0){
							   	scale = 0 - scale;
							   	}
							} catch(Exception e) { System.err.print("Scale entered is invalid");}
			}
			if (evt.getSource() == textBox[4] || evt.getSource() == enterButton[4]){ //spf box and button
					   try {
						       spf = Double.parseDouble(textBox[4].getText());
							} catch(Exception e) { System.err.print("SPF entered is invalid");}
			}

	   }
	   public void start (){

		   th.start ();
		   repaint();
	   }
	   public void stop(){
		   Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
		   th = null;
	   }

	   public void destroy(){
	   }
    @Override
	   public void run (){
	   	th.setPriority(Thread.MIN_PRIORITY);
	   	repaint();
	   	while(true){
	   		th.setPriority(Thread.MIN_PRIORITY);
	   		try{
	   			repaint();
	   			th.sleep(1000);
	   		}//try
	   		catch (InterruptedException ex){
	   		}//catch
	   			while(started){
	   				th.setPriority(Thread.NORM_PRIORITY);
	   					for(int n = 0; n < objects; n++){
	   						if (!stopped[n]){
	   							if(firstDrop[n]){
	   								if (objY[n]- objHeight >= ground && gravity > 0){//if on ground
	   									objY[n] = ground - objHeight;
	   									if(!bounceBox.getState()){
	   									playSound(hitSound);
	   									stopped[n]=true;
	   									}
	   									else{ //if did hit the ground
										   playSound(springSound);
										   bounces[n]++;
										   firstDrop[n] = false;
										   bounceVelocity[n] = restitutionEq(currentVelocity[n],restitution[n]);
										   time[n] = 0;
										   repaint();
										}
	   								}
	   							else{
								   mainTimer += spf; //calculates timer
								   objY[n] = traveledDistanceEq(scale, startY[n], velocity[n], gravity, mainTimer);
								   currentVelocity[n] = finalVelocityEq(velocity[n], gravity, mainTimer);
								   mainTimer = formatDecimal(3, mainTimer); // 3 decimal sig
	   							}//else
	   						}//if firstdrop
	   						else{
							   mainTimer += spf; //calculates timer
							   time[n] += spf;
							   mainTimer = formatDecimal(3, mainTimer); // 3 decimal sig
							   objY[n] = traveledDistanceEq(scale, ground-objHeight, bounceVelocity[n], gravity, time[n]);
							   currentVelocity[n] = finalVelocityEq(bounceVelocity[n], gravity, time[n]);
							   if (objY[n]>= ground-objHeight && gravity > 0 && time[n] >= 0.1){
	   								if (currentVelocity[n] < .9){
									   playSound(hitSound);
									   stopped[n]=true;
	   								}
							   objY[n] = ground-objHeight;
							   bounceVelocity[n] = restitutionEq(currentVelocity[n], restitution[n]);
							   bounces[n]++;
							   playSound(springSound);
							   time[n] = 0;
	   							}
	   						}//if !firstdrop
	   					}//if !stopped
	   				}//for
	   			try{
				   repaint();
				   th.sleep(formatInt(spf * 1000));
	   			}//try
	   			catch (InterruptedException ex){
	   			}//catch
	   		}//while started
	   } //while true
}//run
	   public void mouseClicked(MouseEvent e){
		   int x = e.getPoint().x;
		   int y = e.getPoint().y;
		   repaint();
		   if (y >= 20 && y <= 40){
		   		if (x > 500 && x < 520){
			   		//start
			   		if(started){
						started = false;
						playSound(dropSound);
						repaint();
					}
					else{
						started = true;
						playSound(stopSound);
						repaint();
					}
				}
			   	if (x > 520 && x < 540){
			   		//clear
			   		playSound(resetSound);
			   		reset();
			   		repaint();
			   	}
			   	if (x > 540 && x < 560){
			   		//sound
			   		if(sound){
						sound = false;
						repaint();
					}
					else{
						sound = true;
						repaint();
					}
			   	}
			   	if (x > 560 && x < 580){
					//popup
					window.setVisible(true);
					repaint();
			   	}
		   }

	   }
	   public void mouseEntered(MouseEvent e){
	   }
	   public void mouseExited(MouseEvent e){
	   }
	   public void mousePressed(MouseEvent e){
		   int x = e.getPoint().x;
		   int y = e.getPoint().y;
		   if (x < 80 && x > 30){ //mouse in platform area
			   platformSelected = true;
		   }
		   if (!started){
		   if (x>80 && y<ground && y > 50){
		   startX[objectFocused] = x;
		   startY[objectFocused] = y;
		   objY[objectFocused] = startY[objectFocused];
		   objX[objectFocused] = startX[objectFocused];
		   }}
	   }
	   public void mouseReleased(MouseEvent e){
		   if(platformSelected){
			   platformSelected = false;
		   }
	   }
	   public void mouseMoved(MouseEvent e){
	   }
	   public void mouseDragged(MouseEvent e){
		   if(platformSelected){ //dragging platform
			   platformHeight = e.getPoint().y;
			   if ( platformHeight < 1) {
				   platformHeight = 0;
			   }
			   if (platformHeight >= ground){
				   platformHeight = ground;
			   }
			   repaint();

		   }
	   }
	   public void keyPressed(KeyEvent e) {
	   }
	   public void keyReleased(KeyEvent e) {
	   }
	   public void keyTyped(KeyEvent e) {
	   }

	   public void update (Graphics g){
		   paint(g);
	   }
	   public void paint (Graphics g)
	   	{	 //refreshCount++;
	   		 Dimension d = getSize();
	   		 checkOffscreenImage();
	   		 Graphics offG = mImage.getGraphics();
	   		 offG.setColor(getBackground());
	   		 offG.fillRect(0, 0, d.width, d.height);
	   		 paintOffscreen(mImage.getGraphics());
	      	 g.drawImage(mImage, 0, 0, null);
	   	}
	   	private void checkOffscreenImage() {
	   	    Dimension d = getSize();
	   	    if (mImage == null || mImage.getWidth(null) != d.width
	   	        || mImage.getHeight(null) != d.height) {
	   	      mImage = createImage(d.width, d.height);
	   	    }
	   	  }

	   public void paintOffscreen(Graphics g) {
		   //background
		   g.drawImage(background,0,0,null);
		   //platform
		   g.drawLine(30,ground, 30, platformHeight); //line to platform
           g.drawLine(30,platformHeight, 80, platformHeight); //platform
		   //icons at top
		   if(started){
			   g.drawImage(pause,500,20,null);
		   }
		   else{
			   g.drawImage(play,500,20,null);
		   }
		   g.drawImage(clear,520,20,null);
		   if(sound){
			   g.drawImage(sound_on,540,20,null);
		   }
		   else{
			   g.drawImage(sound_off,540,20,null);
		   }
		   g.drawImage(popup,560,20,null);
		   //ball
		   for(int x = 0; x < objects; x++){
			   if(active[x]){
				   g.drawImage(ball, objX[x], objY[x], null);

			   }
	   		}

	   }

} //class PS
//for options window
class frame extends Frame implements WindowListener, ActionListener{
	frame(String title){
	  super(title);
	  addWindowListener(this);
	  setLayout(new GridLayout(6,1));//rows,columns
	  MenuBar menu = new MenuBar();
	  setMenuBar(menu);
	  setResizable(false);
	  setSize(250,200);
	  Menu fileMenu = new Menu("File");
	  menu.add(fileMenu);
	  MenuItem exitMenu = new MenuItem("Exit");
	  exitMenu.addActionListener(this);
      fileMenu.add(exitMenu);
	}
	public void actionPerformed(ActionEvent e) {
	 		if (e.getSource() == "Exit"){
	 			dispose();
	 			System.exit(0);
			}
	}
	public void windowClosing(WindowEvent e) {

		   	 	dispose();
		   	 	System.exit(0);
		   }

	public void windowOpened(WindowEvent e) {}
	public void windowActivated(WindowEvent e) {}
	public void windowIconified(WindowEvent e) {}
	public void windowDeiconified(WindowEvent e) {}
	public void windowDeactivated(WindowEvent e) {}
	public void windowClosed(WindowEvent e) {}
}

